package com.mk.log.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.plugins.Page;
import com.mk.base.service.impl.BaseService;
import com.mk.common.Config;
import com.mk.common.Const;
import com.mk.common.IPUtils;
import com.mk.log.bean.Log;
import com.mk.log.bean.LogRequest;
import com.mk.log.mapper.LogMapper;
import com.mk.log.service.ILogService;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.javassist.*;
import org.apache.ibatis.javassist.bytecode.CodeAttribute;
import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
import org.apache.ibatis.javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @Author: yq
 * @Description:
 * @Date: Created in 2:11 PM 3/13/18
 */
@Service
public class LogService extends BaseService<LogMapper,Log> implements ILogService {

    private static final String LOG_CONTENT = "[类名]:%s,[方法]:%s,[参数]:%s,[IP]:%s";
    private String username;

    @Override
    public String initUsername(String username) {
        if(StringUtils.isEmpty(username))
            this.username = null;
        else
            this.username = username;
        return this.username;
    }

    @Override
    public void put(JoinPoint joinPoint, String methodName, String module, String description,Integer level) {
        try {
            HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
            String username = Const.USERNAME_IN_CONTEXT != null ? Const.USERNAME_IN_CONTEXT : "未知用户";
            if("未知用户".equals(username) ||
                    (this.username != null && Const.USERNAME_IN_CONTEXT != null
                            && !this.username.equals(Const.USERNAME_IN_CONTEXT))){
                username = this.username;
                this.username = null;
            }
            String ip = IPUtils.getIpAddr(request);
            Log log = new Log();
            log.setUsername(username);
            log.setIp(ip);
            log.setLevel(level);
            log.setModule(module);
            log.setDescription(description);
            log.setContent(operateContent(joinPoint,methodName,ip,request));
            Date nowDate = new Date();
            log.setCreateTime(nowDate);
            log.setUpdateTime(nowDate);
            super.insert(log);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NotFoundException e){
            e.printStackTrace();
        }
    }

    @Override
    public Page<Log> page(LogRequest request, Page<Log> page) {
        List<Log> logs = baseMapper.getPage(request,page);
        page.setRecords(logs);
        return page;
    }

    @Override
    public List<Log> findByCols(LogRequest request) {
        return baseMapper.findByCols(request, Config.ABLE_CONFIG.ABLE);
    }

    private String operateContent(JoinPoint joinPoint, String methodName, String ip, HttpServletRequest request) throws ClassNotFoundException,NotFoundException {
        String className = joinPoint.getTarget().getClass().getName();
        Object[] params = joinPoint.getArgs();
        String classType = joinPoint.getTarget().getClass().getName();
        Class<?> clazz = Class.forName(classType);
        String clazzName = clazz.getName();
        Map<String,Object> nameAndArgs = getFieldsName(this.getClass(),clazzName,methodName,params);
        StringBuffer buff = new StringBuffer();
        if(!CollectionUtils.isEmpty(nameAndArgs)){
            Iterator it = nameAndArgs.entrySet().iterator();
            while (it.hasNext()){
                Map.Entry entry = (Map.Entry) it.next();
                String key = (String) entry.getKey();
                String value = JSONObject.toJSONString(entry.getValue());
                buff.append(key).append("=");
                buff.append(value).append("&");
            }
        }
        if(StringUtils.isEmpty(buff.toString())){
            buff.append(request.getQueryString());
        }
        if(buff.toString().length() >= 4294967295L)
            return String.format(LOG_CONTENT, className, methodName, ip);
        return String.format(LOG_CONTENT, className, methodName, buff.toString(), ip);
    }

    private Map<String,Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
        Map<String,Object> map = new HashMap<>();

        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);

        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if(attr == null){
            //exception
            return map;
        }
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < cm.getParameterTypes().length; i++) {
            map.put(attr.variableName(i + pos),args[i]);
        }
        return map;
    }
}
